#ifndef MATRIZ_H
#define MATRIZ_H

#include <stdlib.h>
#include <vector>
#include <fstream>
#include <math.h>
#include <cmath>
#include <iomanip>
#include <iostream>
#include "../include/TFloat.h"

using namespace std;

/**
* Resta de vectores
* Toma dos vectores de TFloat y devuelve su resta.
*/
vector<TFloat> restar(vector<TFloat>, vector<TFloat>);
TFloat norma2(vector<TFloat>);
TFloat norma1(vector<TFloat>);

class Matriz {
/**
* Matriz
* Agrega funcionalidades útiles a un vector de vectores de TFloat.
*/
public:
    /*********************** Constructores y cargar ***********************/
	/**
    * Constructor por defecto
    */
	Matriz(){matriz.clear();};
	/**
    * Constructor
    * Construye la matriz identidad de n*n.
    */
    Matriz (int n);
	/**
    * Constructor
    * Toma el alto (primer parámetro) y el ancho (segundo parámetro)
    * y devuelve una matriz del tamaño especificado inicializada con ceros
    * y con precisión máxima.
    */
    Matriz (int n, int m);
    /**
    * Constructor
    * Toma el alto (primer parámetro), el ancho (segundo parámetro), y la precisión
    * y devuelve una matriz del tamaño especificado inicializada con ceros.
    */
    Matriz (int n, int m, int precision);
    /**
    * Constructor por copia
    */
	Matriz (const Matriz& otra){
        ancho = otra.ancho;
        alto = otra.alto;
        precision = otra.precision;
        matriz = otra.matriz;
    }
    /**
    * Constructor a partir de un vector
    * Toma un vector no vacío de TFloat y lo castea a matriz de R^(n x 1) (vector columna)
    * con la precisión del vector.
    */
    Matriz (vector<TFloat> v);
    /**
    * Cargar
    * Carga una matriz de un archivo. La primer línea del archivo debe ser,
    * en este orden, dos enteros que especifiquen el alto y el ancho.
    * A partir de la segunda línea deben estar los elementos separados por espacios.
    * No hace falta que haya saltos de línea entre filas.
    */
   	void cargar (std::istream &is, int);

   	/*********************** OPERACIONES entre matrices *****************************/

    /**
    * A menos que se aclare lo contrario, todas las operaciones entre
    * más de una matriz devuelven una matriz con precisión igual a la
    * menos precisa de ambas.
    */

   	/**
    * Trasponer
    * Devuelve una nueva matriz que es la una copia de la original traspuesta.
    */
    Matriz trasponer();
    /**
    * Multiplicar por otra matriz
    * Multiplica 'this'x'otra'.
    */
	Matriz multiplicar(Matriz otra);
	/**
    * Multiplicar por escalar
    * Multiplica 'this'x't'.
    */
   	Matriz multiplicar(TFloat t);
   	/**
    * Sumar
    * Suma dos matrices.
    */
   	Matriz sumar(Matriz otra);
   	/**
    * Restar
    * Resta 'this'-'otra'.
    */
   	Matriz restar(Matriz otra);
    /**
    *
    *
    */
    void operator= (const Matriz& otra);

   	/******************* CÁLCULO DE AUTOVALORES / AUTOVECTORES **********************/

    /**
    * QR
    * Devuelve un vector de matrices con Q como primera componente y R
    * como segunda componente. Requiere que la matriz sea simétrica.
    */
    vector<Matriz> qr();
    vector<Matriz> qr_rapido();

    /**
    * Autovectores
    * Devuelve una matriz con filas iguales a los autovectores de this,
    * ordenados según el módulo del autovalor correspondiente de mayor a menor.
    */
    vector<Matriz> autovalores(TFloat tol, int maxIte);
    Matriz autovectores(TFloat tol, int max_it){return autovalores(tol, max_it)[1];}

    /************************ VARIOS *****************************/

	/**
    * []
    * Ahorra tiempo y espacio al pedirle una posición a la matriz, su alto o su ancho,
    * devolviendo directamente el vector interno por referencia.
    */
	vector<TFloat>& operator[] (int i){return matriz[i];}
    /**
    * Mostrar
    * Muestra una matriz por pantalla.
    */
    void mostrar(std::ostream &os);
    /**
    *
    *
    */
    /**
    *
    *
    */
    vector<TFloat> diagonal();
    Matriz diagonal(vector<TFloat>);

    /**
    * Getters
    */
    vector< vector<TFloat> > dameColumnas();

    /************************ Estructura interna *****************************/
    int alto;
	int ancho;
    size_t precision;

private:
    vector<vector <TFloat> > matriz;

};

#endif
